package org.cqu_proj.demo.service;

import org.cqu_proj.demo.dao.MovieRepository;
import org.cqu_proj.demo.dto.ResponseResult;
import org.cqu_proj.demo.entity.Movie;
import org.cqu_proj.demo.entity.MovieFeatures;
import org.cqu_proj.demo.entity.Rating;
import org.cqu_proj.demo.entity.UserFeatures;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class MovieService {
    //推荐算法的数据
    private final Map<Integer, UserFeatures> userFeatureMap = new ConcurrentHashMap<>();
    private final Map<Integer, MovieFeatures> movieFeatureMap = new ConcurrentHashMap<>();
    //其他服务
    private final RatingService ratingService;
    private final MovieRepository movieRepository;
    private final UserFeaturesService userFeaturesService;
    private final MovieFeaturesService movieFeaturesService;
    private  final UserService userService;
    @Autowired
    private CacheManager cacheManager;
    @Autowired
    public MovieService(RatingService ratingService, MovieRepository movieRepository, UserFeaturesService userFeaturesService, MovieFeaturesService movieFeaturesService, UserService userService) {
        this.ratingService = ratingService;
        this.movieRepository = movieRepository;
        this.userFeaturesService = userFeaturesService;
        this.movieFeaturesService = movieFeaturesService;
        this.userService = userService;
    }




    // 分页查询电影数据
    public Page<Movie> getMovies(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);  // 创建Pageable对象
        return movieRepository.findAll(pageable);  // 返回分页结果
    }

    // <editor-fold desc="对电影进行评论">
    public ResponseResult<Rating> RatingMovie(int userId, int movieId, Double rating){
        ResponseResult<Rating> newSave = this.ratingService.saveOrUpdateRating(userId,movieId,rating);
        boolean isUpdate=true;
        if(newSave.getCode()==200) isUpdate=false;
        else  isUpdate=true;
        boolean temp=updateMovieStats(movieId,isUpdate ? rating - newSave.getData().getRating() : rating,isUpdate);
        this.updateUserMatrixByRatingAction(userId,movieId,rating);

        if(temp){
            return  newSave;
        }
        else  {
            newSave.setCode(203);
            return  newSave;
        }

    }

    //更新电影评分
    @CacheEvict(value = "moviesCache", allEntries = true)
    public boolean updateMovieStats(int movieId, double deltaRating, boolean isUpdate) {
        System.out.println("触发缓存清除");
        Movie movie = movieRepository.findBymovieId(movieId);
        if(movie==null)   {
            System.out.println("电影评分更新时收到错误的movieId");;
            return false;
        }
        if (isUpdate) {
            // 更新评分记录，只需调整平均评分
            movie.setAvgRating(
                    (movie.getAvgRating() * movie.getRatingCount() + deltaRating) / movie.getRatingCount()
            );
        } else {
            // 新增评分记录，增加评论数并调整平均评分
            long newRatingCount = movie.getRatingCount() + 1;
            movie.setAvgRating(
                    (movie.getAvgRating() * movie.getRatingCount() + deltaRating) / newRatingCount
            );
            movie.setRatingCount(newRatingCount);
        }

        // 保存更新后的电影记录
        movieRepository.save(movie);
        return true;
    }
    //根据用户评分进行矩阵更新
    public void updateUserMatrixByRatingAction(int userId, int movieId, Double rating){
        System.out.println("用户评分开始更新矩阵");
        //运算数据准备
        List<Double>userMatrix=this.userFeaturesService.getUserFeaturesByUserId(userId);
        List<Double>movieMatrix=this.movieFeaturesService.getMovieFeaturesAsList(movieId);
        //计算
        List<Double>newUserMatrix=this.movieFeaturesService.sgdForUserFeatures(userMatrix,movieMatrix,rating,0.01,20);
        this.userFeaturesService.updateUserFeatures(userId,newUserMatrix);
        System.out.println("用户评分结束更新矩阵");
    }
    // </editor-fold>

    // <editor-fold desc="genres下：rating排序以及更新矩阵">
    //根据电影genres提供评分排序的分页服务
    @Cacheable(value = "moviesCache", key = "#page + '-' + #size + '-' + #genres")
    public ResponseResult<List<Movie>> getMoviesByGenresAndSortedByRating(List<String> genres, int page, int size) {
        // 构造无序的正则表达式
        String genresRegex = String.join("|", genres);
        System.out.println("service的genres参数："+genresRegex);
        Pageable pageable = PageRequest.of(page, size,Sort.by(Sort.Order.desc("avgRating")));
        return new ResponseResult<List<Movie>>(200,"根据genres和评分分页",movieRepository.findMoviesByGenresSortedByAvgRatingDesc(genresRegex, 20,pageable));
    }
    // 查询所有电影按评分倒序后分页
    @Cacheable(value = "moviesCache", key = "'allMovies-' + #page + '-' + #size")
    public ResponseResult<List<Movie>> getAllMoviesSortedByRating(int page, int size) {
        //Pageable pageable = PageRequest.of(page, size);
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Order.desc("avgRating")));
        return new ResponseResult<>(200,"评分分页",movieRepository.findAllByOrderByAvgRatingDesc(20,pageable));
    }

    public  ResponseResult<List<Movie>>  clickGenres(int userId, String genres,List<String>genresList){
        System.out.println("开始根据genre更新矩阵");
        this.userFeaturesService.updateUserMatrix(userId,genres);
        System.out.println("根据genre更新矩阵完成");
        //根据genres返回电影数据集
        return  this.getMoviesByGenresAndSortedByRating(genresList,0,5);
    }
    public ResponseResult<String> clickMovie(int userId,String genres){
        System.out.println("开始根据genre更新矩阵");
        this.userFeaturesService.updateUserMatrix(userId,genres);
        System.out.println("根据genre更新矩阵完成");
        return ResponseResult.success("根据电影点击更新矩阵成功");
    }
    // </editor-fold>

    // <editor-fold desc="推荐算法实现">
        // <editor-fold desc="数据准备">
            //加载用户特征数据
    private Map<Integer, UserFeatures> loadUserFeaturesFromDatabase() {
        // 从数据库查询并加载用户特征数据
        return this.userFeaturesService.userFeatureRepository.findAll().stream()
                .collect(Collectors.toMap(UserFeatures::getUserId, userFeatures -> userFeatures));
    }

            // 加载电影特征数据
    private Map<Integer, MovieFeatures> loadMovieFeaturesFromDatabase() {
        // 从数据库查询并加载电影特征数据
        List<MovieFeatures> movieFeaturesList=this.movieFeaturesService.movieFeaturesRepository.findAll();
        return movieFeaturesList.parallelStream()
                .collect(Collectors.toMap(MovieFeatures::getMovieId, movieFeatures -> movieFeatures));
    }
    @PostConstruct
    public void initData() {
        // 从数据库加载用户特征和电影特征
        System.out.println("推荐算法的基础数据加载");
        this.userFeatureMap.putAll(loadUserFeaturesFromDatabase());
        this.movieFeatureMap.putAll(loadMovieFeaturesFromDatabase());
    }

        // </editor-fold>
        // <editor-fold desc="需要用到的工具类">
    private double calculateDotProduct(UserFeatures user, MovieFeatures movie) {
        return user.getFeature0() * movie.getFeature1() +
                user.getFeature1() * movie.getFeature2() +
                user.getFeature2() * movie.getFeature3() +
                user.getFeature3() * movie.getFeature4() +
                user.getFeature4() * movie.getFeature5() +
                user.getFeature5() * movie.getFeature6() +
                user.getFeature6() * movie.getFeature7() +
                user.getFeature7() * movie.getFeature8() +
                user.getFeature8() * movie.getFeature9() +
                user.getFeature9() * movie.getFeature10() +
                user.getFeature10() * movie.getFeature11() +
                user.getFeature11() * movie.getFeature12() +
                user.getFeature12() * movie.getFeature13() +
                user.getFeature13() * movie.getFeature14() +
                user.getFeature14() * movie.getFeature15() +
                user.getFeature15() * movie.getFeature16() +
                user.getFeature16() * movie.getFeature17() +
                user.getFeature17() * movie.getFeature18() +
                user.getFeature18() * movie.getFeature19() +
                user.getFeature19() * movie.getFeature20() ;
    }
    // 计算推荐电影列表并缓存
    @Cacheable(value = "recommendations", key = "#userId")
    public List<Integer> calculateRecommendations(int userId) {
        System.out.println("推荐算法计算");
        UserFeatures user = userFeatureMap.get(userId);
        if (user == null) {
            throw new IllegalArgumentException("User not found");
        }

        // 计算每部电影得分
        List<Map.Entry<Integer, Double>> scores = movieFeatureMap.entrySet().parallelStream()
                .map(entry -> new AbstractMap.SimpleEntry<>(
                        entry.getKey(),
                        calculateDotProduct(user, entry.getValue())))
                .sorted((a, b) -> Double.compare(b.getValue(), a.getValue())) // 按得分降序排序
                .collect(Collectors.toList());

        // 提取排序后的电影ID列表
        return scores.stream().map(Map.Entry::getKey).collect(Collectors.toList());
    }
    // 分页获取推荐结果,得到电影Id
    public List<Integer> getPagedRecommendations(int userId, int page, int size,int minRatingCount) {
        System.out.println("是否打印推荐计算");
        List<Integer> recommendations = calculateRecommendations(userId);
        System.out.println("获取分页");
//        int startIndex = page * size;
//        int endIndex = Math.min(startIndex + size, recommendations.size());
//        if (startIndex >= recommendations.size()) {
//            return Collections.emptyList();
//        }
//        return recommendations.subList(startIndex, endIndex);
        // 通过电影ID获取电影信息
        List<Movie> movies = getMoviesByIds(recommendations);
        // 打印前100条推荐电影信息
        System.out.println("推荐的电影信息（前100条）：");
        movies.stream()
                .limit(100)  // 限制打印前100条
                .forEach(movie -> System.out.println(movie)); // 假设 Movie 类重写了 toString 方法
        // 过滤掉ratingCount小于minRatingCount的电影
        List<Integer> filteredRecommendations = movies.stream()
                .filter(movie -> movie.getRatingCount() >= minRatingCount)
                .map(Movie::getMovieId)
                .collect(Collectors.toList());

        // 分页
        int startIndex = page * size;
        int endIndex = Math.min(startIndex + size, filteredRecommendations.size());

        if (startIndex >= filteredRecommendations.size()) {
            return Collections.emptyList();
        }

        return filteredRecommendations.subList(startIndex, endIndex);
    }
    //获取电影数据
    public List<Movie> getMoviesByIds(List<Integer> movieIds) {
        // 假设movieRepository可以根据电影ID列表查询电影信息
        return movieRepository.findAllByMovieIdIn(movieIds);
    }
    //最终实现
    public  ResponseResult<List<Movie>> recommendMovie(int userId, int page ,int size){
        List<Integer>moviesList=this.getPagedRecommendations(userId,page,size,100);
        return  new ResponseResult<>(200,"电影推荐",getMoviesByIds(moviesList));
    }

    // 缓存处理
    public void updateUserFeature(int userId, UserFeatures newFeatures) {
        userFeatureMap.put(userId, newFeatures);
        // 清理缓存
        this.cacheManager.getCache("recommendations").evict(userId);
    }
//
//    public void loadMovieFeatures(List<MovieFeature> movies) {
//        movieFeatureMap.clear();
//        for (MovieFeatures movie : movies) {
//            movieFeatureMap.put(movie.getMovieId(), movie);
//        }
//    }
        // </editor-fold>
    // </editor-fold>


}
